# Disable Flake8 because of all the sphinx imports
#
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements.  See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership.  The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License.  You may obtain a copy of the License at
#
#   http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied.  See the License for the
# specific language governing permissions and limitations
# under the License.
"""Configuration of Providers docs building."""

from __future__ import annotations

# Airflow documentation build configuration file, created by
# sphinx-quickstart on Thu Oct  9 20:50:01 2014.
#
# This file is execfile()d with the current directory set to its
# containing dir.
#
# Note that not all possible configuration values are present in this
# autogenerated file.
#
# All configuration values have a default; values that are commented out
# serve to show the default.
import logging
import os
from pathlib import Path
from typing import Any

import rich
from packaging.version import parse as parse_version

import airflow
from airflow.configuration import retrieve_configuration_description
from docs.utils.conf_constants import (
    AIRFLOW_FAVICON_PATH,
    AIRFLOW_REPO_ROOT_PATH,
    AUTOAPI_OPTIONS,
    BASIC_AUTOAPI_IGNORE_PATTERNS,
    BASIC_SPHINX_EXTENSIONS,
    REDOC_SCRIPT_URL,
    SMARTQUOTES_EXCLUDES,
    SPELLING_WORDLIST_PATH,
    SPHINX_DESIGN_STATIC_PATH,
    SUPPRESS_WARNINGS,
    filter_autoapi_ignore_entries,
    get_autodoc_mock_imports,
    get_configs_and_deprecations,
    get_google_intersphinx_mapping,
    get_html_context,
    get_html_sidebars,
    get_html_theme_options,
    get_intersphinx_mapping,
    get_rst_epilogue,
)
from sphinx_exts.provider_yaml_utils import load_package_data

PACKAGE_NAME = os.environ.get("AIRFLOW_PACKAGE_NAME", "")

ALL_PROVIDER_YAMLS = load_package_data(include_suspended=True)
try:
    CURRENT_PROVIDER = next(
        provider_yaml for provider_yaml in ALL_PROVIDER_YAMLS if provider_yaml["package-name"] == PACKAGE_NAME
    )
except StopIteration:
    raise RuntimeError(f"Could not find provider.yaml file for package: {PACKAGE_NAME}")

PACKAGE_ID = PACKAGE_NAME[len("apache-airflow-providers-") :].replace("-", ".")
PROVIDER_PATH = (AIRFLOW_REPO_ROOT_PATH / "providers").joinpath(*PACKAGE_ID.split("."))
BASE_PROVIDER_SRC_PATH = PROVIDER_PATH / "src" / "airflow"
PACKAGE_VERSION = CURRENT_PROVIDER["versions"][0]
SYSTEM_TESTS_DIR = PROVIDER_PATH / "tests" / "system"

# Adds to environment variables for easy access from other plugins like airflow_intersphinx.
os.environ["AIRFLOW_PACKAGE_NAME"] = PACKAGE_NAME

# Hack to allow changing for piece of the code to behave differently while
# the docs are being built. The main objective was to alter the
# behavior of the utils.apply_default that was hiding function headers
os.environ["BUILDING_AIRFLOW_DOCS"] = "TRUE"

# Use for generate rst_epilog and other post-generation substitutions
global_substitutions = {
    "version": PACKAGE_VERSION,
    "airflow-version": airflow.__version__,
    "experimental": "This is an :ref:`experimental feature <experimental>`.",
}

# == Sphinx configuration ======================================================

# -- Project information -------------------------------------------------------
# See: https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information

# General information about the project.
project = PACKAGE_NAME
# # The version info for the project you're documenting
version = PACKAGE_VERSION
# The full version, including alpha/beta/rc tags.
release = PACKAGE_VERSION

# -- General configuration -----------------------------------------------------
# See: https://www.sphinx-doc.org/en/master/usage/configuration.html

rst_epilog = get_rst_epilogue(PACKAGE_VERSION, False)

smartquotes_excludes = SMARTQUOTES_EXCLUDES

# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones.
extensions = BASIC_SPHINX_EXTENSIONS

PROVIDER_PACKAGES_WITH_REDOC = ["apache-airflow-providers-fab", "apache-airflow-providers-keycloak"]

if PACKAGE_NAME in PROVIDER_PACKAGES_WITH_REDOC:
    extensions.extend(
        [
            "autoapi.extension",
            # First, generate redoc
            "sphinxcontrib.redoc",
            # Second, update redoc script
            "sphinx_script_update",
        ]
    )
    redoc_script_url = REDOC_SCRIPT_URL
else:
    extensions.extend(
        [
            "autoapi.extension",
        ]
    )

extensions.extend(
    [
        "extra_provider_files_with_substitutions",
        "providers_extensions",
        "providers_commits",
        "sphinx_jinja",
    ]
)

# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
empty_subpackages = ["apache", "atlassian", "common", "cncf", "dbt", "microsoft"]
exclude_patterns = [
    "operators/_partials",
    "_api/airflow/index.rst",
    "_api/airflow/providers/index.rst",
    "_api/docs/conf",
    *[f"_api/airflow/providers/{subpackage}/index.rst" for subpackage in empty_subpackages],
    *[f"_api/system/{subpackage}/index.rst" for subpackage in empty_subpackages],
    *[f"_api/tests/system/{subpackage}/index.rst" for subpackage in empty_subpackages],
]

# exclude_patterns.extend(
#     get_rst_filepath_from_path(f, AIRFLOW_REPO_ROOT_PATH)
#     for f in BASE_PROVIDER_SRC_PATH.rglob("example_dags")
# )

# Add any paths that contain templates here, relative to this directory.
templates_path = ["templates"]

# If true, keep warnings as "system message" paragraphs in the built documents.
keep_warnings = True

# -- Options for HTML output ---------------------------------------------------
# See: https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output

# The theme to use for HTML and HTML Help pages.  See the documentation for
# a list of builtin themes.
html_theme = "sphinx_airflow_theme"

html_title = f"{PACKAGE_NAME} Documentation"

# A shorter title for the navigation bar.  Default is the same as html_title.
html_short_title = ""

#  given, this must be the name of an image file (path relative to the
#  configuration directory) that is the favicon of the docs. Modern browsers
#  use this as the icon for tabs, windows and bookmarks. It should be a
#  Windows-style icon file (.ico), which is 16x16 or 32x32 pixels large.
html_favicon = AIRFLOW_FAVICON_PATH.as_posix()

# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = [SPHINX_DESIGN_STATIC_PATH.as_posix()]

html_js_files: list[str] = []

manual_substitutions_in_generated_html = ["example-dags.html", "operators.html", "index.html"]

html_css_files = ["custom.css"]

# -- Theme configuration -------------------------------------------------------
# Custom sidebar templates, maps document names to template names.
html_sidebars = get_html_sidebars(PACKAGE_VERSION)

# If false, no index is generated.
html_use_index = True

# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
html_show_copyright = False

html_theme_options: dict[str, Any] = get_html_theme_options()


conf_py_path = f"/providers/{PACKAGE_ID.replace('.', '/')}/docs/"
# A dictionary of values to pass into the template engine's context for all pages.
html_context = get_html_context(conf_py_path)

# == Extensions configuration ==================================================

# -- Options for sphinx_jinja ------------------------------------------
# See: https://github.com/tardyp/sphinx-jinja

config_descriptions = retrieve_configuration_description(
    include_airflow=False, include_providers=True, selected_provider=PACKAGE_NAME
)

configs, deprecated_options = get_configs_and_deprecations(
    parse_version(PACKAGE_VERSION), config_descriptions
)

PROVIDERS_RELEASE_DATE_PATH = AIRFLOW_REPO_ROOT_PATH / "providers" / ".last_release_date.txt"

PROVIDERS_RELEASE_DATE = PROVIDERS_RELEASE_DATE_PATH.read_text().strip()

jinja_contexts = {
    "config_ctx": {
        "configs": configs,
        "deprecated_options": deprecated_options,
        "package_name": PACKAGE_NAME,
    },
    "official_download_page": {
        "base_url": "https://downloads.apache.org/airflow/providers",
        "closer_lua_url": "https://www.apache.org/dyn/closer.lua/airflow/providers",
        "package_name": PACKAGE_NAME,
        "package_name_underscores": PACKAGE_NAME.replace("-", "_"),
        "package_version": PACKAGE_VERSION,
        "providers_release_date": PROVIDERS_RELEASE_DATE,
    },
}

# -- Options for sphinx.ext.autodoc --------------------------------------------
# See: https://www.sphinx-doc.org/en/master/usage/extensions/autodoc.html

# This value contains a list of modules to be mocked up. This is useful when some external dependencies
# are not met at build time and break the building process.
autodoc_mock_imports = get_autodoc_mock_imports()

# The default options for autodoc directives. They are applied to all autodoc directives automatically.
autodoc_default_options = {"show-inheritance": True, "members": True}

autodoc_typehints = "description"
autodoc_typehints_description_target = "documented"
autodoc_typehints_format = "short"


# -- Options for sphinx.ext.intersphinx ----------------------------------------
# See: https://www.sphinx-doc.org/en/master/usage/extensions/intersphinx.html

# This config value contains names of other projects that should
# be linked to in this documentation.
# Inventories are only downloaded once by docs/exts/docs_build/fetch_inventories.py.
intersphinx_mapping = get_intersphinx_mapping()
if PACKAGE_NAME in ["apache-airflow-providers-google"]:
    intersphinx_mapping.update(get_google_intersphinx_mapping())

# -- Options for sphinx.ext.viewcode -------------------------------------------
# See: https://www.sphinx-doc.org/es/master/usage/extensions/viewcode.html

# If this is True, viewcode extension will emit viewcode-follow-imported event to resolve the name of
# the module by other extensions. The default is True.
viewcode_follow_imported_members = True

# -- Options for sphinx-autoapi ------------------------------------------------
# See: https://sphinx-autoapi.readthedocs.io/en/latest/config.html

# Paths (relative or absolute) to the source code that you wish to generate
# your API documentation from.
autoapi_dirs = [BASE_PROVIDER_SRC_PATH.as_posix()]

# A list of patterns to ignore when finding files
autoapi_ignore = BASIC_AUTOAPI_IGNORE_PATTERNS

autoapi_log = logging.getLogger("sphinx.autoapi.mappers.base")
autoapi_log.addFilter(filter_autoapi_ignore_entries)

autoapi_python_use_implicit_namespaces = True

autoapi_ignore.extend(
    (
        "*/airflow/__init__.py",
        "*/airflow/providers/__init__.py",
        "*/providers/__init__.py",
        "*/conf/*",
    )
)

# Here we remove all other providers from the autoapi list, only leaving the current provider,
# Otherwise all the other provider indexes will no be found in any TOC.

for p in load_package_data(include_suspended=True):
    if p["package-name"] == PACKAGE_NAME:
        continue
    autoapi_ignore.extend((p["package-dir"] + "/*", p["system-tests-dir"] + "/*"))

if SYSTEM_TESTS_DIR and os.path.exists(SYSTEM_TESTS_DIR):
    test_dir = SYSTEM_TESTS_DIR.parent
    autoapi_dirs.append(test_dir.as_posix())

    autoapi_ignore.extend(f"{d}/*" for d in test_dir.glob("*") if d.is_dir() and d.name != "system")

rich.print("[bright_blue]AUTOAPI_IGNORE:")
rich.print(autoapi_ignore)
rich.print()

# Keep the AutoAPI generated files on the filesystem after the run.
# Useful for debugging.
autoapi_keep_files = True

# Relative path to output the AutoAPI files into. This can also be used to place the generated documentation
# anywhere in your documentation hierarchy.
autoapi_root = "_api"

# Whether to insert the generated documentation into the TOC tree. If this is False, the default AutoAPI
# index page is not generated and you will need to include the generated documentation in a
# TOC tree entry yourself.
autoapi_add_toctree_entry = False

# By default autoapi will include private members -- we don't want that!
autoapi_options = AUTOAPI_OPTIONS

suppress_warnings = SUPPRESS_WARNINGS

# -- Options for ext.exampleinclude --------------------------------------------
exampleinclude_sourceroot = os.path.abspath("..")

# -- Options for ext.redirects -------------------------------------------------
redirects_file = "redirects.txt"

# -- Options for sphinxcontrib-spelling ----------------------------------------
spelling_word_list_filename = [SPELLING_WORDLIST_PATH.as_posix()]
spelling_exclude_patterns: list[str] = []
spelling_ignore_contributor_names = False
spelling_ignore_importable_modules = True

graphviz_output_format = "svg"

if PACKAGE_NAME in PROVIDER_PACKAGES_WITH_REDOC:
    from airflow.providers.fab.auth_manager.api_fastapi.openapi import (
        __file__ as fab_auth_manager_fastapi_api_file,
    )
    from airflow.providers.fab.auth_manager.openapi import __file__ as fab_auth_manager_flask_api_file
    from airflow.providers.keycloak.auth_manager.openapi import (
        __file__ as keycloak_auth_manager_fastapi_api_file,
    )

    fab_auth_manager_flask_api_path = Path(fab_auth_manager_flask_api_file).parent.joinpath(
        "v1-flask-api.yaml"
    )
    fab_auth_manager_fastapi_api_path = Path(fab_auth_manager_fastapi_api_file).parent.joinpath(
        "v2-fab-auth-manager-generated.yaml"
    )
    keycloak_auth_manager_fastapi_api_path = Path(keycloak_auth_manager_fastapi_api_file).parent.joinpath(
        "v2-keycloak-auth-manager-generated.yaml"
    )
    redoc = [
        {
            "name": "Fab auth manager API",
            "page": "api-ref/fab-public-api-ref",
            "spec": fab_auth_manager_flask_api_path.as_posix(),
            "opts": {
                "hide-hostname": True,
            },
        },
        {
            "name": "Fab auth manager token API",
            "page": "api-ref/fab-token-api-ref",
            "spec": fab_auth_manager_fastapi_api_path.as_posix(),
            "opts": {
                "hide-hostname": True,
                "no-auto-auth": True,
            },
        },
        {
            "name": "Keycloak auth manager token API",
            "page": "api-ref/token-api-ref",
            "spec": keycloak_auth_manager_fastapi_api_path.as_posix(),
            "opts": {
                "hide-hostname": True,
                "no-auto-auth": True,
            },
        },
    ]
